{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 502 GPU\n",
    "\n",
    "View more, visit my tutorial page: https://mofanpy.com/tutorials/\n",
    "My Youtube Channel: https://www.youtube.com/user/MorvanZhou\n",
    "\n",
    "Dependencies:\n",
    "* torch: 0.1.11\n",
    "* torchvision"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "import torch.utils.data as Data\n",
    "import torchvision\n",
    "\n",
    "torch.manual_seed(1)\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "EPOCH = 1\n",
    "BATCH_SIZE = 50\n",
    "LR = 0.001\n",
    "DOWNLOAD_MNIST = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_data = torchvision.datasets.MNIST(\n",
    "    root='./mnist/', \n",
    "    train=True, \n",
    "    transform=torchvision.transforms.ToTensor(), \n",
    "    download=DOWNLOAD_MNIST,)\n",
    "\n",
    "train_loader = Data.DataLoader(\n",
    "    dataset=train_data, \n",
    "    batch_size=BATCH_SIZE, \n",
    "    shuffle=True)\n",
    "\n",
    "test_data = torchvision.datasets.MNIST(\n",
    "    root='./mnist/', train=False)\n",
    "\n",
    "# !!!!!!!! Change in here !!!!!!!!! #\n",
    "test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1)).type(torch.FloatTensor)[:2000].cuda()/255.   # Tensor on GPU\n",
    "test_y = test_data.test_labels[:2000].cuda()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class CNN(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(CNN, self).__init__()\n",
    "        self.conv1 = nn.Sequential(\n",
    "            nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5, stride=1, padding=2,),                      \n",
    "            nn.ReLU(), \n",
    "            nn.MaxPool2d(kernel_size=2),)\n",
    "        self.conv2 = nn.Sequential(\n",
    "            nn.Conv2d(16, 32, 5, 1, 2), \n",
    "            nn.ReLU(), \n",
    "            nn.MaxPool2d(2),)\n",
    "        self.out = nn.Linear(32 * 7 * 7, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.conv1(x)\n",
    "        x = self.conv2(x)\n",
    "        x = x.view(x.size(0), -1)\n",
    "        output = self.out(x)\n",
    "        return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "CNN (\n",
       "  (conv1): Sequential (\n",
       "    (0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))\n",
       "    (1): ReLU ()\n",
       "    (2): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))\n",
       "  )\n",
       "  (conv2): Sequential (\n",
       "    (0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))\n",
       "    (1): ReLU ()\n",
       "    (2): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))\n",
       "  )\n",
       "  (out): Linear (1568 -> 10)\n",
       ")"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cnn = CNN()\n",
    "\n",
    "# !!!!!!!! Change in here !!!!!!!!! #\n",
    "cnn.cuda()      # Moves all model parameters and buffers to the GPU."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "optimizer = torch.optim.Adam(cnn.parameters(), lr=LR)\n",
    "loss_func = nn.CrossEntropyLoss()\n",
    "\n",
    "losses_his = []"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Training"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch:  0 | train loss: 2.3145 | test accuracy: 0.10\n",
      "Epoch:  0 | train loss: 0.5546 | test accuracy: 0.83\n",
      "Epoch:  0 | train loss: 0.5856 | test accuracy: 0.89\n",
      "Epoch:  0 | train loss: 0.1879 | test accuracy: 0.92\n",
      "Epoch:  0 | train loss: 0.0601 | test accuracy: 0.94\n",
      "Epoch:  0 | train loss: 0.1772 | test accuracy: 0.95\n",
      "Epoch:  0 | train loss: 0.0993 | test accuracy: 0.94\n",
      "Epoch:  0 | train loss: 0.2210 | test accuracy: 0.95\n",
      "Epoch:  0 | train loss: 0.0379 | test accuracy: 0.96\n",
      "Epoch:  0 | train loss: 0.0535 | test accuracy: 0.96\n",
      "Epoch:  0 | train loss: 0.0268 | test accuracy: 0.96\n",
      "Epoch:  0 | train loss: 0.0910 | test accuracy: 0.96\n",
      "Epoch:  0 | train loss: 0.0982 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.3154 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.0418 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.1072 | test accuracy: 0.96\n",
      "Epoch:  0 | train loss: 0.0652 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.1042 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.1346 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.0431 | test accuracy: 0.98\n",
      "Epoch:  0 | train loss: 0.0276 | test accuracy: 0.97\n",
      "Epoch:  0 | train loss: 0.0153 | test accuracy: 0.98\n",
      "Epoch:  0 | train loss: 0.0438 | test accuracy: 0.98\n",
      "Epoch:  0 | train loss: 0.0082 | test accuracy: 0.97\n"
     ]
    }
   ],
   "source": [
    "for epoch in range(EPOCH):\n",
    "    for step, (x, y) in enumerate(train_loader):\n",
    "\n",
    "        # !!!!!!!! Change in here !!!!!!!!! #\n",
    "        b_x = Variable(x).cuda()    # Tensor on GPU\n",
    "        b_y = Variable(y).cuda()    # Tensor on GPU\n",
    "\n",
    "        output = cnn(b_x)\n",
    "        loss = loss_func(output, b_y)\n",
    "        losses_his.append(loss.data[0])\n",
    "        optimizer.zero_grad()\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "\n",
    "        if step % 50 == 0:\n",
    "            test_output = cnn(test_x)\n",
    "\n",
    "            # !!!!!!!! Change in here !!!!!!!!! #\n",
    "            pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze()  # move the computation in GPU\n",
    "\n",
    "            accuracy = torch.sum(pred_y == test_y).type(torch.FloatTensor) / test_y.size(0)\n",
    "            print('Epoch: ', epoch, '| train loss: %.4f' % loss.data[0], '| test accuracy: %.2f' % accuracy)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x7f5b979dcb70>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(losses_his, label='loss')\n",
    "plt.legend(loc='best')\n",
    "plt.xlabel('Steps')\n",
    "plt.ylabel('Loss')\n",
    "plt.ylim((0, 1))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      " 7\n",
      " 2\n",
      " 1\n",
      " 0\n",
      " 4\n",
      " 1\n",
      " 4\n",
      " 9\n",
      " 5\n",
      " 9\n",
      "[torch.cuda.LongTensor of size 10 (GPU 0)]\n",
      " prediction number\n",
      "\n",
      " 7\n",
      " 2\n",
      " 1\n",
      " 0\n",
      " 4\n",
      " 1\n",
      " 4\n",
      " 9\n",
      " 5\n",
      " 9\n",
      "[torch.cuda.LongTensor of size 10 (GPU 0)]\n",
      " real number\n"
     ]
    }
   ],
   "source": [
    "# !!!!!!!! Change in here !!!!!!!!! #\n",
    "test_output = cnn(test_x[:10])\n",
    "pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze() # move the computation in GPU\n",
    "\n",
    "print(pred_y, 'prediction number')\n",
    "print(test_y[:10], 'real number')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
